<?php
namespace douyin;
use commonsdk\Tools;
use commonsdk\Video_range;
use GuzzleHttp\Client;
use think\exception\HttpResponseException;
use think\exception\ValidateException;
use think\facade\Log;
use tools\HttpPost;
use think\facade\Env;

class Dy
{
    const URL_CONNECT = 'https://open.douyin.com/platform/oauth/connect';
    const URL_TOKEN = 'https://open.douyin.com/oauth/access_token';
    const URL_REFRESH_TOKEN = 'https://open.douyin.com/oauth/refresh_token/';
    const URL_REFRESH_REFRESH_TOKEN = 'https://open.douyin.com/oauth/renew_refresh_token/';
    const URL_USER_INFO = 'https://open.douyin.com/oauth/userinfo/';
    const URL_USER_FANS_LIST = 'https://open.douyin.com/fans/list/';
    const URL_USER_FOLLOW_LIST = 'https://open.douyin.com/following/list/';
    const URL_VIDEO_LIST = 'https://open.douyin.com/video/list/';
    const URL_VIDEO_DATA = 'https://open.douyin.com/video/data/';
    const URL_VIDEO_UPLOAD = 'https://open.douyin.com/video/upload/';
    const URL_VIDEO_UPLOAD_PART_INIT = 'https://open.douyin.com/video/part/init/';
    const URL_VIDEO_UPLOAD_PART_UPLOAD = 'https://open.douyin.com/video/part/upload/';
    const URL_VIDEO_UPLOAD_PART_COMPLETE = 'https://open.douyin.com/video/part/complete/';
    const URL_VIDEO_CREATE = 'https://open.douyin.com/video/create/';

    const URL_VIDEO_INFO = 'https://open.douyin.com/data/external/item/base/';
    const URL_COUPON_SYNC = 'https://open.douyin.com/poi/v2/coupon/sync/coupon_available/';

    static public $scopes = [
        //视频权限
        'aweme.share', //抖音分享
        'video.create',
        'video.delete',
        'video.data',
        'video.list',
        //        'toutiao.video.create',
        //        'toutiao.video.data',
        //        'xigua.video.data',
        //        'xigua.video.create',
        //'im. share',
        //'video.search',
        //'video.search.comment',
        //用户权限
        'user_info',
        //'following.list',
        //'fans.list',
        //'login_id',
        // 'renew_refresh_token',
        //'mobile_alert',
        //互动权限
        //'video.comment', //企业号评论
        //'im', //企业私信
        //'item.comment',
        //数据权限
        // 'hotsearch',
        // 'fans.data',
        // 'data.external.user',
        //'data.external.item',
        //'star_top_score_display',
        //'star_tops',
        //'star_author_score_display',
        //'data.external.sdk_share',
        //特殊权限
        //'share_with_source',
        //'poi.search',
        //'micapp.is_legal',
    ];
    public $client_key;
    public $client_secret;

    public function __destruct() {

    }

    /**
     * 获取access_token
     * @param $code
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function getAccessToken($code){
        $openApi = self::URL_TOKEN;
        $config = get_sysset();
        $postData = ['multipart' => [
            [
                'name'     => 'client_secret',
                'contents' => $config['basic']['client_secret'],
            ],
            [
                'name'     => 'code',
                'contents' => $code,
            ],
            [
                'name'     => 'grant_type',
                'contents' => 'authorization_code',
            ],
            [
                'name'     => 'client_key',
                'contents' => $config['basic']['client_key'],
            ]
        ]];
        $data = HttpPost::post($openApi,$postData);
        if($data['data']['error_code'] != 0){
            throw new ValidateException($data['data']['description']);
        }
        return $data;
    }

    /**
     * 获取用户信息
     * @param $openid
     * @param $accessToken
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function getUserInfo($openid,$accessToken){
        $data = HttpPost::get(self::URL_USER_INFO.'?open_id='.$openid.'&access_token='.$accessToken,[]);
        if($data['data']['error_code'] != 0){
            throw new ValidateException($data['data']['description']);
        }
        return $data;
    }

    /**
     * 发布视频
     * @param $openid
     * @param $access_token
     * @param $sale
     * @param $video
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function releaseVideo($openid,$access_token,$sale, $video){
        $video = self::uploadVideo($openid,$access_token,$video);
        $video_id = $video['data']['video']['video_id'];
        $openApi = self::URL_VIDEO_CREATE.'?open_id='.$openid.'&access_token='.$access_token;
        $param = [
            'video_id'=>$video_id,//上传的视频ID
            'text' => $sale['text'] ?? '',
            'micro_app_id' => (isset($sale['micro_app_id']))?$sale['micro_app_id']:"", //小程序id
            'poi_id' => (isset($sale['poi_id']))?$sale['poi_id']:"", //地理位置id
            'poi_name' => (isset($sale['poi_name']))?$sale['poi_name']:"", //地理位置名称
            'micro_app_title' => (isset($sale['micro_app_title']))?$sale['micro_app_title']:"", //小程序标题
            'micro_app_url' => (isset($sale['micro_app_url']))?$sale['micro_app_url']:"", //	吊起小程序时的url参数
        ];
        $postData = ['json' => $param];
        $data = HttpPost::post($openApi,$postData);
        if($data['extra']['error_code'] != 0){
            throw new ValidateException($data['data']['description']);
        }
        return $data;
    }

    /**
     * 上传视频
     */
    public static function uploadVideo($openid,$access_token,$video){
        $openApi = self::URL_VIDEO_UPLOAD.'?open_id='.$openid.'&access_token='.$access_token;
        $postData = ['multipart' => [
            [
                'name'     => 'video',
                'contents' => file_get_contents(Env::get('root_path').'public/'.$video),
                'headers'  =>['Content-Type'=>'video/mp4'],
                'filename'=>'aa.mp4'
            ]
        ]
        ];
        $data = HttpPost::post($openApi,$postData);
        if($data['data']['error_code'] != 0){
            throw new ValidateException($data['data']['description']);
        }
        return $data;
    }

    /**
     * 同步视频数据
     */
    public static function syncVideo($openid,$access_token,$item_id){
        $openApi = self::URL_VIDEO_INFO;
        $queryData = ['query' => [
            'open_id' => $openid,
            'access_token'=>$access_token,
            'item_id'=>urlencode($item_id)
            ]
        ];
        $data = HttpPost::get($openApi,$queryData);
        if($data['extra']['error_code'] != 0){
            throw new ValidateException($data['data']['description']);
        }
        return $data;
    }

    /**
     * 同步优惠券
     * @param $token
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public static function couponSync($token){
        $openApi = self::URL_COUPON_SYNC.'?access_token='.$token;
        $param = [
            'video_id'=>$video_id,//上传的视频ID
            'text' => $sale['text'] ?? '',
            'micro_app_id' => (isset($sale['micro_app_id']))?$sale['micro_app_id']:"", //小程序id
            'poi_id' => (isset($sale['poi_id']))?$sale['poi_id']:"", //地理位置id
            'poi_name' => (isset($sale['poi_name']))?$sale['poi_name']:"", //地理位置名称
            'micro_app_title' => (isset($sale['micro_app_title']))?$sale['micro_app_title']:"", //小程序标题
            'micro_app_url' => (isset($sale['micro_app_url']))?$sale['micro_app_url']:"", //	吊起小程序时的url参数
        ];
        $postData = ['json' => $param];
        $data = HttpPost::post($openApi,$postData);

    }

    /**
     * 获取视频详情
     * @param string $access_token
     * @param string $open_id
     * @param string $item_id
     * @return mixed
     * @throws \Exception
     */
    public function get_video_info($access_token,$open_id,$item_id)
    {
        $arr = [
            'access_token' => $access_token,
            'open_id' => $open_id,
            'item_id'=>$item_id
        ];
        $response = file_get_contents(self::URL_VIDEO_INFO . '?' . http_build_query($arr));
        $data = self::checkResponse($response);
        return $data;
    }

    /**
     * 通过网页授权获取粉丝信息
     */
    public function getWebOauthInfo($redirects, $redirect = true): array {
        if (input('get.code') == "") {
            $oauthurl = self::buildConnectUrl($redirects);
            // echo $oauthurl;exit;
            if ($redirect) {
                throw new HttpResponseException(redirect($oauthurl, 302));
            } else {
                throw new HttpResponseException(response("window.location.href='{$oauthurl}'"));
            }
        }
        if (($token_arr = $this->getTokenByCode(input('get.code')))) {
            $fansinfo = self::getAuthUserInfo($token_arr['access_token'], $token_arr['open_id']);
            $fansinfo['access_token'] = $token_arr['access_token'];
            return $fansinfo;
        }
    }

    //创建授权跳转链接  获取code
    public function buildConnectUrl($redirect = "") {
        $arr = [
            'client_key' => $this->client_key,
            'scope' => implode(',', self::$scopes),
            'redirect_uri' => $redirect,
            'response_type' => 'code',
            'state' => "",
        ];
        return self::URL_CONNECT . '?' . http_build_query($arr);
    }

    //刷新refresh_token
    public function refreshRefreshToken($authAccount) {
        $arr = [
            'client_key' => $this->client_key,
            'refresh_token' => $authAccount['refresh_token'],
        ];
        $response = file_get_contents(self::URL_REFRESH_REFRESH_TOKEN . '?' . http_build_query($arr));
        $data = self::checkResponse($response);
        $authAccount['refresh_token'] = $data['refresh_token'];
        $authAccount['refresh_expires_in'] = $data['refresh_expires_in'];
        if (!Db::name('dy_accesstoken')->update($authAccount)) {
            throw new \Exception(json_encode(Db::name('dy_accesstoken')->getErrors(), JSON_UNESCAPED_UNICODE));
        }
    }

    //刷新token
    public function refreshToken($authAccount) {
        $arr = [
            'client_key' => $this->client_key,
            'grant_type' => 'refresh_token',
            'refresh_token' => $authAccount['refresh_token'],
        ];
        $response = file_get_contents(self::URL_REFRESH_TOKEN . '?' . http_build_query($arr));
        $data = self::checkResponse($response);
        $authAccount['token_expire_time'] = $data['expires_in'];
        if (!Db::name('dy_accesstoken')->update($authAccount)) {
            throw new \Exception(json_encode(Db::name('dy_accesstoken')->getErrors(), JSON_UNESCAPED_UNICODE));
        }
    }

    //获取token
    public function getTokenByCode($code) {
        $arr = [
            'client_key' => $this->client_key,
            'client_secret' => $this->client_secret,
            'code' => $code,
            'grant_type' => 'authorization_code',
        ];
        //echo self::URL_TOKEN . '?' . http_build_query($arr);
        $response = file_get_contents(self::URL_TOKEN . '?' . http_build_query($arr));
        $data = self::checkResponse($response);
        return $data;
    }

    public function getAuthUserInfo($access_token, $open_id) {
        $arr = [
            'access_token' => $access_token,
            'open_id' => $open_id,
        ];
        $response = file_get_contents(self::URL_USER_INFO . '?' . http_build_query($arr));
        $data = self::checkResponse($response);
        return $data;
    }

    //发布
    public function publishItem($authAccount, $authItem) {
        //大于6M用分片上传,否则用普通上传
        // if ($authItem['size'] > 6000000) {
        //     $data = $this->uploadPart($authAccount, $authItem);
        // } else {
        $data = $this->upload($authAccount, $authItem);
        //}
        $video_id = $data['video']['video_id'];
        $query_arr = [
            'open_id' => $authAccount['open_id'],
            'access_token' => $authAccount['token'],
        ];
        $body = json_encode([
            'video_id' => $video_id,
            'text' => $authItem['text'],
            'micro_app_id' => (isset($authItem['micro_app_id']))?$authItem['micro_app_id']:"", //小程序id
            'poi_id' => (isset($authItem['poi_id']))?$authItem['poi_id']:"", //地理位置id
            'poi_name' => (isset($authItem['poi_name']))?$authItem['poi_name']:"", //地理位置名称
            'micro_app_title' => (isset($authItem['micro_app_title']))?$authItem['micro_app_title']:"", //小程序标题
            'micro_app_url' => (isset($authItem['micro_app_url']))?$authItem['micro_app_url']:"", //	吊起小程序时的url参数
        ], JSON_UNESCAPED_UNICODE);
        $response = Tools::http_post_json(self::URL_VIDEO_CREATE . '?' . http_build_query($query_arr), $body);
        $data = self::checkResponse($response[1]);
        return $data;
    }

    //普通上传
    private function upload($authAccount, $video) {
        $query_arr = [
            'open_id' => $authAccount['open_id'],
            'access_token' => $authAccount['token'],
        ];
        $Video_range=new Video_range();
        // $content = $Video_range->getPartData($video['path'], 0, 6000000);
        $content = file_get_contents($video['path']);
        $body_arr = Tools::buildDouyinUploadData($content);

        $response = Tools::postUploadData(
            self::URL_VIDEO_UPLOAD . '?' . http_build_query($query_arr),
            $body_arr['data'],
            0,
            ['Content-Type:multipart/form-data;boundary=' . $body_arr['boundary']]
        );
        $data = self::checkResponse($response);
        return $data;
    }

    //分片上传
    private function uploadPart($authAccount, $video) {
        $query_arr = [
            'open_id' => $authAccount['open_id'],
            'access_token' => $authAccount['token'],
        ];
        $body = '';

        $response = Tools::http_post_json(self::URL_VIDEO_UPLOAD_PART_INIT . '?' . http_build_query($query_arr), $body);
        $data = self::checkResponse($response);
        $upload_id = $data['upload_id'];
        $part = 1;
        $part_size = 6000000;
        while ($part) {
            $start = ($part - 1) * $part_size;
            if ($start >= $video['size']) {
                break;
            }

            //如果剩余不足一个分片则一并上传
            $yu = $video['size'] - $start - $part_size;
            if ($yu < $part_size) {
                $part_size += $yu;
            }
            $content = Video_range::getPartData($video['path'], $start, $part_size);
            $body_arr = Tools::buildDouyinUploadData($content);

            $query_arr = [
                'open_id' => $authAccount['open_id'],
                'access_token' => $authAccount['token'],
                'upload_id' => $upload_id,
                'part_number' => $part,
            ];

            $response = Tools::postUploadData(
                self::URL_VIDEO_UPLOAD_PART_UPLOAD . '?' . http_build_query($query_arr),
                $body_arr['data'],
                0,
                [
                    'Content-Type:multipart/form-data;boundary=' . $body_arr['boundary'],
                ]
            );
            $data2 = self::checkResponse($response);
            $part++;
        }
        $query_arr = [
            'open_id' => $authAccount['open_id'],
            'access_token' => $authAccount['token'],
            'upload_id' => $upload_id,
        ];

        $response = Tools::http_post_json(self::URL_VIDEO_UPLOAD_PART_COMPLETE . '?' . http_build_query($query_arr));
        $data = self::checkResponse($response);
        return $data;
    }

    //检测并格式化抖音返回数据
    static public function checkResponse($response) {
        $arr = json_decode($response, 1);
        if (empty($arr))
            throw new \Exception('抖音接口返回错误', 20000);
        $data = $arr['data'];
        if (!isset($data['error_code']))
            throw new \Exception('抖音接口返回错误', 20000);
        if ($data['error_code'] != 0) {
            throw new \Exception('抖音接口返回错误:' . $data['error_code'] . ":" . $data['description'], 20000);
        }
        return $data;
    }
}